//------------------------------------------------------------------------
// <copyright file="FileExplorer.cs" company="DevDir">
//   Copyleft (c) DevDir. No rights reserved.
// </copyright>
// <author>Nikita Sharov</author>
//------------------------------------------------------------------------

namespace DevDir.Mp3
{
  using System;
  using System.IO;
  using System.Collections.Generic;
  using System.Windows.Forms;
  
  public class FileExplorer
  {
    private static readonly FileExplorer instance = new FileExplorer();

    private const int folderImageIndex = 0;
    private const int openFolderImageIndex = 1;
    private const int hardDriveImageIndex = 2;

    private TreeView treeView;
    private DriveInfo[] drives = DriveInfo.GetDrives();
    private string currentPath;
    private Dictionary<string, TreeNode> nodes = 
      new Dictionary<string, TreeNode>();
    private MetaDataTable data = new MetaDataTable();

    private FileExplorer()
    {
    }

    public static FileExplorer Instance
    {
      get { return instance; }
    }

    public TreeView TreeView
    {
      get 
      { 
        return this.treeView; 
      }

      set
      {
        if (this.treeView != value)
        {
          this.treeView = value;
          this.OnTreeViewChanged();
        }
      }
    }

    public MetaDataTable Data
    {
      get { return this.data; }
    }

    public void Refresh()
    {
      nodes.Clear();

      // TODO: Handle unready devices.
      if (currentPath != null)
      {
        DirectoryInfo directory = new DirectoryInfo(currentPath);
        TreeNode rootNode = new TreeNode();
        rootNode.Name = directory.FullName;
        rootNode.Text = directory.Name;
        Fill(rootNode, true);
        TreeView.Nodes.Add(rootNode);
      }
      else
      {
        foreach (DriveInfo drive in drives)
        {
          if (drive.DriveType == DriveType.Fixed)
          {
            TreeNode driveNode = new TreeNode();
            driveNode.Name = drive.Name;
            driveNode.Text = drive.Name.Remove(2);
            driveNode.ImageIndex = hardDriveImageIndex;
            driveNode.SelectedImageIndex = hardDriveImageIndex;
            Fill(driveNode, true);
            TreeView.Nodes.Add(driveNode);
          }
        }
      }
    }

    public void Scan()
    {
      this.data.Clear();
      Scan(this.currentPath, Settings.RecursiveDirectories);
      this.data.AcceptChanges();
    }

    protected virtual void OnTreeViewChanged()
    {
      this.treeView.AfterSelect += 
        new TreeViewEventHandler(this.TreeViewAfterSelect);
      this.treeView.AfterExpand += 
        new TreeViewEventHandler(this.TreeViewAfterExpand);
    }

    private void Scan(string path, bool recursive)
    {
      if (recursive)
      {
        string[] directories = Directory.GetDirectories(path);
        foreach (string directory in directories)
        {
          this.Scan(directory, recursive);
          this.data.AcceptChanges();
        }
      }

      string[] files = Directory.GetFiles(path);
      foreach (string file in files)
      {
        FileInfo fileInfo = new FileInfo(file);
        if (Settings.SupportedExtensions.Contains(fileInfo.Extension))
        {
          MetaDataFile metaDataFile = new MetaDataFile(fileInfo);
          MetaDataRow row = (MetaDataRow)data.NewRow();          
          row.Set(metaDataFile);
          data.Rows.Add(row);
        }
      }
    }

    private void Fill(TreeNode node, bool subdirectories)
    {
      try
      {
        string[] directoryPaths = Directory.GetDirectories(node.Name);
        node.Nodes.Clear();
        foreach (string path in directoryPaths)
        {
          DirectoryInfo directory = new DirectoryInfo(path);
          TreeNode directoryNode = new TreeNode();
          directoryNode.Name = directory.FullName;
          directoryNode.Text = directory.Name;
          if (subdirectories)
          {
            Fill(directoryNode, false);
          }

          node.Nodes.Add(directoryNode);
          if (!nodes.ContainsKey(directoryNode.Name))
          {
            nodes.Add(directoryNode.Name, directoryNode);
          }
        }
      }
      catch (UnauthorizedAccessException)
      {
        // access to some folders may be denied
      }
    }

    private bool IsDriveNode(TreeNode node)
    {
      foreach (DriveInfo drive in drives)
      {
        if (node.Name == drive.Name)
        {
          return (true);
        }
      }
      return (false);
    }

    private void TreeViewAfterSelect(object sender, TreeViewEventArgs e)
    {
      if ((e.Action != TreeViewAction.Collapse) &&
        (e.Action != TreeViewAction.Unknown))
      {
        e.Node.Expand();
      }
      currentPath = e.Node.Name;
    }

    private void TreeViewAfterExpand(object sender, TreeViewEventArgs e)
    {
      if (!IsDriveNode(e.Node))
      {
        Fill(e.Node, true);
      }
    }
  }
}
